//========================================================= 
/**@file TankUI.h
 * @brief 
 * 
 * @date 2025-07-18   12:04:19
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_TANKUI_H_
#define _LIBZHOUYB_TANKUI_H_
//--------------------------------------------------------- 
#include "Tank.h"

#include "../../tools/AsyncWorker.h"
using zhou_yb::application::tools::AsyncWorker;

#include "../../../wrapper/imgui/imgui_extractor.h"
using zhou_yb::wrapper::imgui_extractor::GuiWindow;

#include "../../../extension/ability/win_helper/WinCharsetConvert.h"
using zhou_yb::extension::ability::WinCharsetConvert;

#include "../../../include/Extension.h"

#define LOG LogCat_Default("")
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace game {
namespace tank90 {
//--------------------------------------------------------- 
struct TankGuiMeta
{
    std::string name;
    uint color;
    bool visible;

    TankGuiMeta() : name(""), color(0), visible(true) {}
};
//--------------------------------------------------------- 
enum enum_TankReward
{
    reward_None = 0,
    reward_Power,
    reward_Hp,
    reward_Bomb,
    reward_Life,
};
//--------------------------------------------------------- 
class TankBlinkReward : public TankReward
{
protected:
    byte _reward_type;
    TickTimer _timer;
public:
    TankBlinkReward()
    {
        _reward_type = reward_None;
        _timer.SetTimeout(5);
    }
    virtual void OnFrame()
    {
        TankReward::OnFrame();

        if (_timer.Elapsed())
        {
            _timer.Restart();

            TankGuiMeta* m = (TankGuiMeta*)meta;
            m->visible = !m->visible;
        }
    }

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TankReward::OnSerialize(se);
        se << _reward_type;

        TankGuiMeta* m = (TankGuiMeta*)meta;
        se << _timer.TickCount() << m->visible;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TankReward::OnDeserialize(se);
        se << _reward_type;

        TankGuiMeta* m = (TankGuiMeta*)meta;
        uint tick = 0;
        se << tick << m->visible;
        _timer.Forward(tick);
    }

    virtual void Apply(Tank& tank)
    {
        switch (_reward_type)
        {
        case reward_Hp:
            tank.Hp = TANK_DEFAULT_HP;
            break;
        case reward_Power:
            if (tank.Power < 2)
            {
                tank.Power += 1;
                tank.MagazineClip += 1;
                tank.speed = TANK_MOVE_SPEED + tank.Power * TANK_POWER_SPEED;
            }
            break;
        default:
            break;
        }
    }
};
//--------------------------------------------------------- 
struct ITankRender
{
    virtual void OnDraw(TBody2D* body, TankGuiMeta* meta) = 0;
};
//--------------------------------------------------------- 
class TankGuiRender : public ITankRender, public RefObject
{
public:
    ImVec2 Position;
    uint ZoomSize;
public:
    TankGuiRender() : ZoomSize(1) {}

    virtual void OnDraw(TBody2D* body, TankGuiMeta* meta) override
    {
        ImDrawList* dw = ImGui::GetWindowDrawList();
        ImU32 color = (meta->color | 0xFF000000);
        ImVec2 p1 = ImVec2(body->position.x / ZoomSize, body->position.y / ZoomSize) + Position;
        ImVec2 p2 = p1;

        uint w = body->size.x / ZoomSize;
        uint h = body->size.y / ZoomSize;
        p2.x += w;

        p2.y += h;

        TNode2D* node = (TNode2D*)body;
        if (node->layer == mask_Enemy || node->layer == mask_Player)
        {
            Tank* tank = (Tank*)body;
            uint barrel_size = 40 / ZoomSize;
            ImVec2 p;
            if (node->direction == TVector2_UP)
            {
                p = p1;
                p.x += (w - barrel_size) / 2;
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);

                p1.y += barrel_size;
            }
            else if (node->direction == TVector2_DOWN)
            {
                p = p1;
                p.x += (w - barrel_size) / 2;
                p.y += h - barrel_size;
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);

                p2.y -= barrel_size;
            }
            else if (node->direction == TVector2_LEFT)
            {
                p = p1;
                p.y += (h - barrel_size) / 2;

                p1.x += barrel_size;
            }
            else if (node->direction == TVector2_RIGHT)
            {
                p = p1;
                p.x += w - barrel_size;
                p.y += (h - barrel_size) / 2;

                p2.x -= barrel_size;
            }
            if (p.x || p.y)
            {
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);
            }

            ImGui::PushStyleColor(ImGuiCol_Text, 0xFFFFBF00);
            ImGui::SetCursorPos(ImVec2(p1.x, p1.y - 10));
            ImGui::Text("HP:%d", tank->Hp);
            if (!meta->name.empty())
            {
                ImGui::SetCursorPos(ImVec2(p1.x, p1.y + 10));
                ImGui::Text("%s", meta->name.c_str());
            }
            ImGui::PopStyleColor();
        }

        dw->AddRectFilled(p1, p2, color);
    }
};
//--------------------------------------------------------- 
class TankEnginObserver : public ITEngineObserver, public RefObject
{
protected:
    std::vector<Ref<ITankRender> > _renders;
public:
    void AddRender(Ref<ITankRender> render)
    {
        _renders.push_back(render);
    }

    virtual void OnInit(TBody2D* body) override
    {
        TankGuiMeta* meta = new TankGuiMeta();
        switch (body->layer)
        {
        case mask_Player:
            meta->color = RGB(34, 144, 255);
            break;
        case mask_Enemy:
            meta->color = RGB(232, 232, 232);
            break;
        case mask_Barrier:
            meta->color = RGB(220, 220, 220);
            break;
        case mask_Reward:
            meta->color = RGB(255, 215, 0);
            break;
        case mask_Bullet:
            meta->color = RGB(255, 250, 250);
            break;
        }
        body->meta = meta;
    }

    virtual void OnFini(TBody2D* body) override
    {
        if (body->meta)
        {
            delete body->meta;
            body->meta = NULL;
        }
    }

    virtual void OnFrame(TBody2D* body) override
    {
        ((TNode2D*)body)->OnFrame();
    }

    virtual void OnUpdate(TBody2D* body) override
    {
        
    }

    virtual void OnCollision(TBody2D* body, list_t<TCollisionData2D>& data) override
    {
        ((TNode2D*)body)->OnCollision(data);
    }

    virtual void OnFixedFrame(list_t<TBody2D*>& bodies) override
    {
        for (list_t<TBody2D*>::iterator itr = bodies.begin(); itr != bodies.end(); ++itr)
        {
            TankGuiMeta* meta = (TankGuiMeta*)(*itr)->meta;
            if (meta && meta->visible)
            {
                for (int i = 0;i < _renders.size(); ++i)
                {
                    _renders[i]->OnDraw(*itr, meta);
                }
            }
        }
    }
};
//--------------------------------------------------------- 
class TankUI : public GuiWindow, public LoggerBehavior, public ITankFactory, public RefObject
{
public:
    static byte GetInputVector2(ImGuiKey w, ImGuiKey s, ImGuiKey a, ImGuiKey d)
    {
        if (ImGui::IsKeyDown(a))
        {
            return move_Left;
        }
        if (ImGui::IsKeyDown(d))
        {
            return move_Right;
        }
        if (ImGui::IsKeyDown(w))
        {
            return move_Up;
        }
        if (ImGui::IsKeyDown(s))
        {
            return move_Down;
        }
        return move_None;
    }
protected:
    TankGame _game;
    TankGame _watch_game;
    TankRecorder _recorder;
    TankEnginObserver _observer1;
    TankEnginObserver _observer2;
    TankGuiRender _render1;
    TankGuiRender _render2;
    TankPlayer* _player;

    byte _key_direction;
    byte _key_shoot;

    bool _is_replay;
    uint _replay_speed;
    uint _replay_frame_id;

    void _init()
    {
        _render1.Position = ImVec2(0, 0);
        _render1.ZoomSize = 10;

        _render2.Position = ImVec2(280, 0);
        _render2.ZoomSize = 10;

        _observer1.AddRender(_render1);
        _observer2.AddRender(_render2);

        _game.SetObserver(_observer1);
        _watch_game.SetObserver(_observer2);

        // 绘制边框
        _game.Init(26, 24, *this);
        
        _player = (TankPlayer*)_game.Place(_game.Instantiate(mask_Player), 2, 3);
        _game.Place(_game.Instantiate(mask_Enemy), 4, 6);
        _game.Place(_game.Instantiate(mask_Reward), 7, 8);

        _key_direction = 0;
        _key_shoot = 0;

        _is_replay = false;
        _replay_speed = 2;
        _replay_frame_id = 0;
    }
    void _ShowWindow()
    {

    }
public:
    TankUI()
    {
        _init();
    }

    virtual void OnRender()
    {
        ImGui::SetNextWindowPos(ImVec2(0, 0));
        ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);
        ImGui::Begin("Tank", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize);
        _ShowWindow();

        if (_key_direction == 0)
        {
            _key_direction = GetInputVector2(ImGuiKey_W, ImGuiKey_S, ImGuiKey_A, ImGuiKey_D);
        }
        if (_key_shoot == 0)
        {
            _key_shoot = ImGui::IsKeyPressed(ImGuiKey_J) ? 1 : 0;
        }

        _game.Render();
        ImGui::SetCursorPos(ImVec2(0, 260));
        ImGui::Text("%u", _recorder.GetCurrentFrameId());

        if (_is_replay)
        {
            _watch_game.Render();
            ImGui::SetCursorPos(ImVec2(280, 260));
            ImGui::Text("%u", _replay_frame_id);
        }

        ImGui::End();
    }
    virtual bool FixedUpdate(Timer::TickType tick, uint delta)
    {
        if (ImGui::IsKeyDown(ImGuiKey_Q))
        {
            if (GuiWindow::IsRunning())
            {
                GuiWindow::Stop();
            }
            return false;
        }

        _recorder.AcceptInput(_game, _player->Uid, _key_direction, _key_shoot);
        _key_direction = 0;
        _key_shoot = 0;

        _game.Update();
        _recorder.Record(_game);

        if (ImGui::IsKeyDown(ImGuiKey_B))
        {
            const TankFrame& frame = _recorder.Seek(0);
            _replay_frame_id = 0;
            TankGame::LoadSnapshoot(frame.snapshoot, _watch_game, *this);

            _is_replay = true;
        }
        if (_is_replay)
        {
            if (!_recorder.Replay(_watch_game, _replay_speed, &_replay_frame_id))
                _replay_speed = 1;
        }
        return true;
    }

    virtual TNode2D* Create(uint class_type) override
    {
        switch (class_type)
        {
        case mask_Player:
            return new TankPlayer();
        case mask_Barrier:
            return new TNode2D();
        case mask_Enemy:
            return new TankEnemy();
        case mask_Reward:
            return new TankBlinkReward();
        case mask_Bullet:
            return new TankBullet();
        }
        return NULL;
    }

    virtual void Destroy(TNode2D* node) override
    {
        delete node;
    }
};
//--------------------------------------------------------- 
} // namespace tank90
} // namespace game
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
 #endif // #ifndef _LIBZHOUYB_TANKUI_H_
//========================================================= 